package com.beta.cat.utils.sequence.range.impl.redis;

import com.beta.cat.service.RedissonService;
import com.beta.cat.utils.sequence.exception.SeqException;
import com.beta.cat.utils.sequence.range.SeqRange;
import com.beta.cat.utils.sequence.range.SeqRangeMgr;
import org.springframework.util.Assert;

/**
 * Redis区间管理器
 * Created by fctao on 2018/5/8.
 */
public class RedisSeqRangeMgr implements SeqRangeMgr {

    /**
     * 前缀防止key重复
     */
    private final static String KEY_PREFIX = "x_sequence_inc_";

    /**
     * redis客户端
     */
    private RedissonService redissonService;

    /**
     * 区间步长
     */
    private int step = 1000;

    /**
     * 区间起始位置，真实从stepStart+1开始
     */
    private long stepStart = 0;

    /**
     * 标记业务key是否存在，如果false，在取nextRange时，会取check一把
     * 这个boolean只为提高性能，不用每次都取redis check
     */
    private volatile boolean keyAlreadyExist;

    @Override
    public SeqRange nextRange(String name) throws SeqException {
        String realKey = getRealKey(name);
        if (!keyAlreadyExist) {
            String stepStart = redissonService.getStr(realKey);
            if (stepStart == null) {
                String realKeyLock = realKey + "_lock";
                if (!redissonService.tryLock(realKeyLock, 500, 200)) {
                    throw new SeqException("同时获取失败，请重新获取!");
                }
                try {
                    stepStart = redissonService.getStr(realKey);
                    if (stepStart == null) {
                        //第一次不存在，进行初始化,setnx不存在就set，存在就忽略
                        redissonService.setStr(realKey, getStepStart() + "");
                    }
                } finally {
                    redissonService.unlock(realKeyLock);
                }
            }
            keyAlreadyExist = true;
        }

        Long max = redissonService.incrBy(realKey, step);
        Long min = max - step + 1;
        return new SeqRange(min, max);
    }

    @Override
    public void init() {
        Assert.isTrue(stepStart >= 0, "stepStart不能为负数");
        Assert.notNull(redissonService, "redissonService is null");
    }

    public RedissonService getRedissonService() {
        return redissonService;
    }

    public void setRedissonService(RedissonService redissonService) {
        this.redissonService = redissonService;
    }

    public int getStep() {
        return step;
    }

    public void setStep(int step) {
        this.step = step;
    }

    private String getRealKey(String name) {
        return KEY_PREFIX + name;
    }

    private boolean isEmpty(String str) {
        return null == str || str.trim().length() == 0;
    }


    public long getStepStart() {
        return stepStart;
    }

    public void setStepStart(long stepStart) {
        this.stepStart = stepStart;
    }
}
